iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0

文章同步發表至 Medium

接續前一天的縣市找景點功能,複習一下我們會有的功能說明:

  • 身為一個使用者,我可以點擊地圖上的查詢按鈕,選擇縣市之後找到該縣市的景點。
  • 身為一個使用者,查詢縣市景點之後,地圖上只會呈現該縣市的景點。

昨天已經建立好 API,今天要做的就是顯示查詢的頁面,以及第二點重新渲染的部分。

1. 查詢縣市的頁面顯示

這邊會使用到的套件有:Bootstrap 5Sweet Alert 2,都是我在工作上沒有特別要求前端葉面的時候會使用到的工具,可以輕鬆地就完成一個簡單乾淨的頁面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>旅遊規劃</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.8.0/dist/leaflet.css"
          integrity="sha512-hoalWLoI8r4UszCkZ5kL8vayOGVae1oxXe/2A4AO6J9+580uKHDO3JdHb7NzwwzK5xr/Fs0W40kiNHxM9vyTtQ=="
          crossorigin=""/>
    <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.Default.css">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    
    <style>
        body{
            height: 100vh;
        }
        
        #app, .row, #map{
            height: 100%;
        }
        
        #search-by-county{
            z-index: 1001;
            top: 1%;
            right: 0;
        }
    </style>
</head>
<body>

<div id="app" class="container-fluid">
    <div class="row align-items-center position-relative">
        <div id="map"></div>
        <div id="search-by-county" class="position-absolute col-auto">
            <button type="button" class="btn btn-lg btn-primary">
                查詢
            </button>
        </div>
    </div>
</div>

</body>
</html>

我想做到的效果是地圖上會有一個固定的查詢按鈕,但因為 Leaflet 預設地圖的 z-index 是 1000,所以必須把按鈕的數值再調大一點。成品如下:

有了按鈕之後,我們還需要先透過 Vue 來建立一個查詢的流程,包含:

  1. 點擊後打開一個視窗
  2. 選擇要查詢的縣市

這個部分我選擇直接用 Sweet Alert 的方式製作。首先要先加入點擊按鈕之後會出發的 alert,我是參考官方網站的範例進行修改的:

<head>
    <meta charset="UTF-8">
    <title>旅遊規劃</title>
    <!-- 略 -->
</head>
<body>
<div id="app" class="container-fluid">
    <div class="row align-items-center position-relative">
        <div id="map"></div>
        <div id="search-by-county" class="position-absolute col-auto">
            <button type="button" class="btn btn-lg btn-primary"
                    @click.prevent="searchScenicByCounty">
                <!-- ^^^ 新增點擊事件,綁定 function 名稱 -->
                查詢
            </button>
        </div>
    </div>
</div>

<script>
    const app = Vue.createApp({
        data(){},
        methods:{
            initMap(){
                // ...
            },
            getScenicSpots(){
                // ...
            },
            searchScenicByCounty(){
                Swal.fire({
                    title: '利用縣市搜尋景點',
                    
                    // 輸入的類型,select = 下拉式選單
                    input: 'select',
                    
                    // 選單的內容,物件名稱為 <option> 中的 value
                    inputOptions: {
                        A: '臺北市',
                        B: '臺中市',
                        C: '基隆市',
                        D: '臺南市',
                        E: '高雄市',
                        F: '新北市',
                        G: '宜蘭縣',
                        H: '桃園市',
                        I: '嘉義市',
                        J: '新竹縣',
                        K: '苗栗縣',
                        M: '南投縣',
                        N: '彰化縣',
                        O: '新竹市',
                        P: '雲林縣',
                        Q: '嘉義縣',
                        T: '屏東縣',
                        U: '花蓮縣',
                        V: '臺東縣',
                        W: '金門縣',
                        X: '澎湖縣',
                        Z: '連江縣',
                    },
                    
                    // 顯示取消的按鈕和文字設定
                    showCancelButton: true,
                    cancelButtonText: '取消',
                    
                    // 顯示確認的按鈕文字設定
                    confirmButtonText: '查詢'
                }).then((result) => {
                    if (result.isConfirmed) {
                        // 按下的按鈕是確認(查詢)的按鈕:打 API
                        axios({
                            method: 'get',
                            url: `./api/ScenicSpot/${result.value}`
                        }).then(res => {
                            console.log(res.data);
                        }).catch(err => {
                            console.log(err);
                        })
                    }
                })
            },
        },
        mounted(){
            this.getScenicSpots();
        }
    });
    app.mount('#app');
</script>
</body>
</html>

重新整理網頁後應該就會看到 console 印出了下列的訊息:

好耶!那我們是不是可以直接套用前幾天的 initMap(),渲染到地圖上了呢?

2. 重新渲染景點

前幾天所使用到的地圖初始化功能在這裡:

initMap(data){
    // 設定地圖
    let map = L.map('map').setView([24.175339, 120.648586], 19);
    
    // 新增圖磚
    L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);

    // 建立點位
    let layer = data.map(ele => { L.marker([ele.y, ele.x]).addTo(map) });

    // 新增點位到圖磚上
    map.addLayer(layer);
}

那我們是不是只要直接套用就可以了呢?

const app = Vue.createApp({
    data(){},
    methods:{
        initMap(data){
            // ...
        },
        getScenicSpots(){
            // ...
        },
        searchScenicByCounty(){
            Swal.fire({
               // ...
            }).then((result) => {
                if (result.isConfirmed) {
                    axios({
                        method: 'get',
                        url: `./api/ScenicSpot/${result.value}`
                    }).then(res => {
                        // 初始化地圖
                        this.initMap(res.data);
                    }).catch(err => {
                        console.log(err);
                    })
                }
            })
        },
    },
    mounted(){
        this.getScenicSpots();
    }
});
app.mount('#app');

如果你很開心的這樣做,然後重新整理頁面,按下查詢之後,你應該會發現的你的 console 出現了一個 error:

Error: Map container is already initialized.

由於我們在 Vue 掛載時就有用 L.map('map') 先初始化過地圖了,所以重複呼叫的時候就會出現這個錯誤。

Reference


上一篇
旅遊規劃小專案 4 - 縣市找景點
下一篇
旅遊規劃小專案 6 - 重新渲染景點
系列文
歡迎來到 GIS 的世界!30 天從後端開始學 GIS30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言